#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Log.hpp"
using namespace LogModule;

const std::string sep = "\r\n";
const std::string Linesep = ": ";
const std::string BlankSep = " ";
const std::string defaulthomepage = "wwwroot/index.html";
const std::string default_404_page = "wwwroot/404.html";
const std::string defaultHttpVersion = "HTTP/1.1";
const std::string defaulthomedir = "wwwroot";
const std::string successpage = "/success.html";
const std::string errorspage = "/404.html";

// 检查是否是一个完整的http请求
bool Decode(std::string &packge, std::string *out)
{
    // 如果找不到空行就返回false
    // 这里我们就认为post会设置"Content-Length"，如果长度不满足返回false
    auto pos = packge.find("\r\n\r\n");
    if (pos == std::string::npos)
        return false;
    std::string tmp = "Content-Length";
    auto pos1 = packge.find(tmp);
    int full_path = pos + 4; // 如果有内容就直接加上即可
    if (pos1 != std::string::npos)
    {
        // Content-Length: 6\r\n
        auto pos2 = packge.find(sep, pos1 + tmp.size()); // 数字的最后一个位置
        int n = pos2 - pos1 - tmp.size() - Linesep.size();
        int len = std::stoi(packge.substr(pos1 + tmp.size() + Linesep.size(), n)); // 长度字符
        if (pos + 4 + len > packge.size())
            return false; // 说明此时post内容还没有读取完善
        full_path = full_path + len;
    }
    // 其余情况，我们都认为读取完善了。
    *out = packge.substr(0, full_path);
    packge.erase(0, full_path);
    return true;
}

class Request
{
private:
    std::string _method;
    std::string _url;
    std::string _version;
    std::vector<std::string> _req_header;
    std::string _blank_line;
    std::string _body;
    std::unordered_map<std::string, std::string> _header_kv;
    bool ParseHeadLine(std::string &package, const std::string &sep)
    {
        auto pos = package.find(sep);
        if (pos == std::string::npos)
        {
            LOG(LogLevel::ERROR) << "没有请求报头";
            return false;
        }
        std::string line = package.substr(0, pos);
        std::stringstream ss(line);
        // stringstream复制使用 >> 符号
        ss >> _method >> _url >> _version;
        package.erase(0, pos + sep.size());
        return true;
    }
    bool ParseOneLine(std::string &line, const std::string &LineSep)
    {
        auto pos = line.find(LineSep);
        if (pos == std::string::npos)
            return false;
        std::string k = line.substr(0, pos);
        std::string v = line.substr(pos + LineSep.size());
        _header_kv[k] = v;
        return true;
    }
    bool ParseHeaderReq(std::string &package, const std::string &sep, const std::string &Linesep)
    {
        while (true)
        {
            auto pos = package.find(sep);
            if (pos == 0)
            {
                LOG(LogLevel::ERROR) << "Find Blank line ,dealig it";
                _blank_line = sep;
                package.erase(0, sep.size());
                break;
            }
            std::string line = package.substr(0, pos);
            _req_header.push_back(line);
            if (!ParseOneLine(line, Linesep))
            {
                LOG(LogLevel::ERROR) << "行解析失败";
                return false;
            }
            package.erase(0, pos + sep.size());
        }
        return true;
    }

public:
    Request() {}
    std::string GetBody() const { return _body; }
    void SetUrl(const std::string &url)
    {
        _url = url;
    }
    std::string GetUrl() const
    {
        LOG(LogLevel::INFO) << "_url: " << _url << "back(): " << _url.back();
        std::string url;
        if (_url == "/")
        {
            url = defaulthomepage;
        }
        else
        { // 我们得到的url会自动添加 /
            url = defaulthomedir + _url;
        }
        return url;
    }
    bool Deserilize(std::string &package)
    {
        // 读取请求报头
        if (!ParseHeadLine(package, sep))
            return false;
        // 读去请求行
        if (!ParseHeaderReq(package, sep, Linesep))
            return false;
        _body = package;
        package.erase(0, _body.size());
        return true;
    }
    bool IsHaveArgs() const
    {
        return (_url.find("?") != std::string::npos) || (!_body.empty());
    }
    std::string GetTask()
    {
        if (_url.find("?"))
        {
            return _url.substr(0, _url.find("?"));
        }
        else
        {
            return _url;
        }
    }
    std::string Method() const { return _method; }
    void DebugPrint()
    {
        LOG(LogLevel::INFO) << "-------------------请求报头--------------";
        LOG(LogLevel::INFO) << "method: " << _method << " url: " << _url << " version: " << _version;
        LOG(LogLevel::INFO) << "-------------------请求报头结束--------------";
        LOG(LogLevel::INFO) << "--------------------请求行---------------------";
        for (auto &s : _req_header)
        {
            LOG(LogLevel::INFO) << s;
        }
        LOG(LogLevel::INFO) << "--------------------请求行end---------------------";
        LOG(LogLevel::INFO) << "--------------------空行---------------------";
        LOG(LogLevel::INFO) << _blank_line;
        LOG(LogLevel::INFO) << "--------------------空行END---------------------";
        LOG(LogLevel::INFO) << "--------------------内容---------------------";
        LOG(LogLevel::INFO) << _body;
        LOG(LogLevel::INFO) << "--------------------内容end---------------------";
    }
    ~Request() {}
};

class Response
{
private:
    std::string _url;
    int _status;
    std::unordered_map<std::string, std::string> _head_kv;
    std::vector<std::string> _relevant_header;
    std::string _blan_line;
    std::string _body;
    std::string _res_head;
    std::unordered_map<std::string, std::string> _SuffType;

    std::string GetContent(const std::string &path) const
    {
        std::string content;
        std::ifstream in(path.c_str(), std::ios::binary);
        if (!in.is_open())
            return std::string();
        in.seekg(0, in.end);       // 定位文件末尾
        int fullsize = in.tellg(); // 求出文件大小
        content.resize(fullsize);
        in.seekg(0, in.beg);
        in.read((char *)content.c_str(), fullsize);
        in.close();
        return content;
    }
    std::string Status2String()
    {
        switch (_status)
        {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        default:
            return "OK";
        }
        return "OK";
    }
    void InitSuff()
    {
        _SuffType[".html"] = "text/html";
        _SuffType[".jpg"] = "application/x-jpg";
        _SuffType[".txt"] = "text/plain";
    }
    std::string Suff2String(const std::string &url)
    {
        auto pos = url.rfind(".");
        if (pos == std::string::npos)
        {
            return "text/html";
        }
        std::string type = url.substr(pos);
        if (_SuffType.find(type) == _SuffType.end())
            return "text/html";
        return _SuffType[type];
    }
    void SetKv(const std::string &k, const std::string &v)
    {
        _head_kv[k] = v;
    }
    void SetRelevantHeader()
    {
        for (auto &kv : _head_kv)
        {
            _relevant_header.push_back(kv.first + Linesep + kv.second + sep);
        }
    }

public:
    Response()
    {
        InitSuff();
    }
    std::string Build(const Request &req)
    {
        std::string url = req.GetUrl();
        LOG(LogLevel::INFO) << "research url: " << url;

        _body = GetContent(url);
        if (_body.empty())
        {
            _body = GetContent(default_404_page);
            url = default_404_page;
            _status = 404;
        }
        else
        {
            _status = 200;
        }

        // 构建应答了可以
        _res_head = defaultHttpVersion + BlankSep + std::to_string(_status) + BlankSep + Status2String() + sep;
        std::string filetype = Suff2String(url);
        if (!_body.empty())
        {
            std::string length = std::to_string(_body.size());
            SetKv("Content-Length", length);
        }
        SetKv("Content-Type", filetype);
        // 构建相应报头
        SetRelevantHeader();
        std::string retstr = _res_head;
        for (auto &x : _relevant_header)
            retstr += x;
        retstr += (_blan_line + sep);
        retstr += _body;
        return retstr;
    }
    ~Response() {}
};